using System;
using Syndication.Extensibility;
using System.Windows.Forms;
using System.Xml;
using System.Xml.XPath;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Diagnostics;

namespace NGNntpPlugIn
{
	/// <summary>
	/// Summary description for PlugIn.
	/// </summary>
	public class PlugIn : IBlogExtension
	{
		const string SEPARATOR = "\x01";

		public PlugIn() : base()
		{
		}

		#region IBlogExtension Members

		public void BlogItem(System.Xml.XPath.IXPathNavigable rssFragment, bool edited)
		{
			PostBlogItem(rssFragment, edited);
		}

		public bool HasEditingGUI
		{
			get
			{
				// TODO:  Add PlugIn.HasEditingGUI getter implementation
				return false;
			}
		}

		public string DisplayName
		{
			get
			{
				string fileDesc;
				Version version;
				object[] versionNumbers;
				fileDesc = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileDescription;
				version = Assembly.GetExecutingAssembly().GetName().Version;
				versionNumbers = new object[5];
				versionNumbers[0] = fileDesc;
				versionNumbers[1] = version.Major;
				versionNumbers[2] = version.Minor;
				versionNumbers[3] = version.Build;
				versionNumbers[4] = version.Revision;
				return string.Format("{0} v{1}.{2}.{3}.{4}", versionNumbers);
			}
		}

		public void Configure(System.Windows.Forms.IWin32Window parent)
		{
			ShowConfig(parent);
		}

		public bool HasConfiguration
		{
			get
			{
				return true;
			}
		}

		#endregion

		#region Private Methods

		private void ShowConfig(System.Windows.Forms.IWin32Window parent)
		{
			Configuration config = new Configuration();

			config.ShowDialog(parent);
		}

		private void PostBlogItem(System.Xml.XPath.IXPathNavigable rssFragment, bool edited)
		{
			string description = "";
			string title = "";
			string server = "";
			string newsgroups = "";
			string messageId = "";
			string feedName = "";
			string customData = "";
			string userName = "";
			string password = "";
			bool prompt = true;
			bool hasCancelled = false;
			NntpConfig config = new NntpConfig();
			NntpServerCollection servers = new NntpServerCollection();
			bool customDataFound = false;

			// verify the configuration has been set
			if(config.DisplayName == "")
			{
				// create the new configuration dialog
				Configuration configDlg = new Configuration();

				// show the dialog
				configDlg.ShowDialog();

				// reload the configuration
				config.LoadConfig();
			}

			// get the xpath navigator
			XPathNavigator xnav = rssFragment.CreateNavigator();

			DebugAlert("Retrieving post item information.");

			// get an iterator for the custom data
			XPathNodeIterator xit = xnav.Select("/rss/channel/item");
			while(xit.MoveNext())
			{
				// move to the first child
				if(xit.Current.HasChildren)
					xit.Current.MoveToFirstChild();

				// see if this child is the description
				if(xit.Current.Name.ToLower() == "description")
					description = xit.Current.Value;

				// see if this child is the title
				if(xit.Current.Name.ToLower() == "title")
					title = xit.Current.Value;

				// see if this child is the custom data
				if(xit.Current.Name.ToLower() == "ng:customdata")
					customData = xit.Current.Value;

				while(xit.Current.MoveToNext())
				{
					// move to the next sibling

					// see if this child is the description
					if(xit.Current.Name.ToLower() == "description")
						description = xit.Current.Value;

					// see if this child is the title
					if(xit.Current.Name.ToLower() == "title")
						title = xit.Current.Value;

					// see if this child is the custom data
					if(xit.Current.Name.ToLower() == "ng:customdata")
						customData = xit.Current.Value;
				}
			}

			DebugAlert("Cleaning HTML");

			// strip html tags
			string stripped = Regex.Replace(description, @"</p.*?>", System.Environment.NewLine + System.Environment.NewLine);
			stripped = Regex.Replace(stripped, @"</*br.*?>", System.Environment.NewLine);
			stripped = Regex.Replace(stripped, @"<.*?>", "");
			stripped = stripped.Replace("&lt;", "<").Replace("&gt;", ">").Replace("&quot;", "\"").Replace("&ldquo;", "\"").Replace("&rdquo;", "\"").Replace("&rsquo;", "'").Replace("&nbsp;", " ").Replace("&amp;", "&");

			DebugAlert("HTML Cleaned");

			if(stripped.Trim() == "")
			{
				// no description is present, message cannot be posted
				MessageBox.Show("Your message had no content.  Your message was not posted.", "Error Posting", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			else
			{
				// there is a description, processing can continue

				DebugAlert("Retrieved post item information.");

				// get the application data path
				string appDataLoc = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData);

				DebugAlert("Opening OPML File");

				// load the OPML file
				XmlDocument opmlDoc = new XmlDocument();
            opmlDoc.Load(appDataLoc + @"\NewsGator\Inbox\NewsGatorSubs.opml");

				DebugAlert("Opened OPML File");

				// split the custom data
				if(customData != "")
				{
					DebugAlert("Retrieving the Custom Data");
					// retrieve the custom data for the nntp
					try
					{
						// load the custom data into an xml document
						XmlDocument custDoc = new XmlDocument();
						custDoc.LoadXml("<customData>" + customData + "</customData>");

						XmlNode node = custDoc.DocumentElement.SelectSingleNode("./data/eworldnntp/messageid");
						if(node != null)
							messageId = node.InnerText;

						node = custDoc.DocumentElement.SelectSingleNode("./data/eworldnntp/server");
						if(node != null)
							server = node.InnerText;

						node = custDoc.DocumentElement.SelectSingleNode("./data/eworldnntp/newsgroups");
						if(node != null)
							newsgroups = node.InnerText;

						node = custDoc.DocumentElement.SelectSingleNode("./data/eworldnntp/feedname");
						if(node != null)
							feedName = node.InnerText;

						customDataFound = true;
					}
					catch
					{
						try
						{
							// included for backwards support
							string[] data = customData.Split(Convert.ToChar(SEPARATOR));
		
							// set the message id
							messageId = data[0];
		
							// set the server
							server = data[1];
		
							// set the newsgroups
							newsgroups = data[2];
		
							// set the feed name
							feedName = data[3];

							customDataFound = true;
						}
						catch
						{
							// this is not available
							customDataFound = false;
						}
					}
				
					if(customDataFound)
					{
						DebugAlert("Custom Data Found");
						// this is a reply to a post, find its feed in the opml to determine credentials
						XmlNode feedNode = opmlDoc.SelectSingleNode("opml/body/outline[@title='" + feedName + "']");

						if(feedNode != null)
						{
							// we found the feed node, get the username and password
							foreach(XmlNode att in feedNode.Attributes)
							{
								if(att.Name.ToLower() == "ng:username")
									userName = att.Value;
								if(att.Name.ToLower() == "ng:passwordenc")
									password = att.Value;
							}
						}

						prompt = config.AlwaysPromptWhenReplying;
					}
					else
					{
						DebugAlert("No Custom Data Found");
						prompt = true;
					}
				}
				else
					// this is not a reply
					messageId = "";

				if(prompt)
				{
					// prompt the user to select the newsgroups
					NewsgroupSelection selection = new NewsgroupSelection(opmlDoc, server, newsgroups);

					// show the dialog
					selection.ShowDialog();

					if(selection.DialogResult == DialogResult.OK)
					{
						// set the values
						servers = selection.SelectedServers;
						hasCancelled = false;
					}
					else
						// the user cancelled
						hasCancelled = true;
				}
				else
				{
					// add the reply server to the collection
					servers.Add(new NntpServer(server, newsgroups, userName, password));
				}

				DebugAlert("Number of servers selected for posting: " + servers.Count.ToString());

				if(!hasCancelled)
				{
					DebugAlert("Continuing to Post Message");

					// loop through each of the servers to send the message
					foreach(NntpServer sendServer in servers)
					{
						if(sendServer.IsNewsgroupSelected())
						{
							// this server has at least 1 newsgroup selected
							DebugAlert("Attempting to post to: " + sendServer.Server);

							Nntp poster = new Nntp();

							try
							{
								DebugAlert("Connecting to Server");
								// connect to the server
								if(sendServer.Authenticate)
									poster.Connect(sendServer.Server, sendServer.UserName, sendServer.Password);
								else
									poster.Connect(sendServer.Server);
								DebugAlert("Connected to Server");

								// generate a listing of newsgroups
								newsgroups = "";
								foreach(NntpNewsgroup ng in sendServer.Newsgroups)
								{
									if(ng.IsSelected)
									{
										if(newsgroups != "")
											newsgroups += ",";
										newsgroups += ng.Name;
									}
								}

								DebugAlert("Using Newsgroup(s): " + newsgroups);

								DebugAlert("Posting to Server");
								// post to the newsgroups
								if(messageId == "")
									poster.Post(newsgroups, title, config.DisplayName, stripped);
								else
									poster.Post(newsgroups, title, config.DisplayName, stripped, messageId);
								DebugAlert("Posted Message");

								DebugAlert("Disconnecting from Server");
								// disconnect from the server
								poster.Disconnect();
								DebugAlert("Disconnected from Server");
							}
							catch(NntpException nEx)
							{
								DebugAlert("An error occurred: " + nEx.Message);

								// an error occurred, determine what caused the error
								string errorNumber = nEx.Message.Substring(0, 3);

								if(errorNumber == "201")
								{
									// posting is not allowed
									MessageBox.Show("Posting is not allowed to the newsgroup(s): '" + newsgroups + "'", "Error Posting to Newsgroup", MessageBoxButtons.OK, MessageBoxIcon.Error);
								}
								else if(errorNumber == "400")
								{
									// service is discontinued
									MessageBox.Show("The News Server has been discontinued. Your message was not posted.", "Error Posting to Newsgroup", MessageBoxButtons.OK, MessageBoxIcon.Error);
								}
								else if(errorNumber == "500")
								{
									// command not recognized
									AlertUserOfError("There has been an internal error, and your message was not posted.  Would you like to contact the developer for more help?", nEx, sendServer.Server, newsgroups);
								}
								else if(errorNumber == "501")
								{
									// command syntax error
									AlertUserOfError("There has been an internal error, and your message was not posted.  Would you like to contact the developer for more help?", nEx, sendServer.Server, newsgroups);
								}
								else if(errorNumber == "502")
								{
									// access restriction or permission denied
									MessageBox.Show("Authentication is required and you have not supplied the correct credentials. Your message was not posted.", "Error Posting to Newsgroup", MessageBoxButtons.OK, MessageBoxIcon.Error);
								}
								else if(errorNumber == "503")
								{
									// program fault - command not performed
									AlertUserOfError("There has been an internal error, and your message was not posted.  Would you like to contact the developer for more help?", nEx, sendServer.Server, newsgroups);
								}
								else
								{
									// unknown error
									AlertUserOfError("An unknown error occurred while posting the message.  Would you like to contact the developer for more help?", nEx, sendServer.Server, newsgroups);
								}
								break;
							}
							catch (Exception ex)
							{
								DebugAlert("An error occurred: " + ex.Message);
								// unknown error
								AlertUserOfError("An unknown error occurred while posting the message.  Would you like to contact the developer for more help?", ex, sendServer.Server, newsgroups);
								break;
							}
						}
					}
				}
			}

			config.CheckForUpdate(false);
		}

		private void AlertUserOfError(string msg, Exception ex, string server, string newsgroups)
		{
			if(MessageBox.Show(msg, "Error Posting to Newsgroup", MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.Yes)
			{
				System.Diagnostics.Process.Start("mailto:support@eworldui.net?subject=NNTP%20Plugin%20Problem&body=" + System.Web.HttpUtility.UrlEncode("--------------------------------\n\nError%20Message:" + ex.Message + "\nServer: " + server + "\nNewsgroup(s): " + newsgroups + "\n--------------------------------"));
			}
		}

		private void DebugAlert(string message)
		{
#if DEBUG
			MessageBox.Show("DEBUG: " + message);
#endif
		}

		#endregion
	}
}
